/*
 * Copyright (C) 2011-2013 Eric Lasota
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#ifndef __RDX_RUNTIME_HPP__
#define __RDX_RUNTIME_HPP__

#include "rdx_processing.hpp"
#include "rdx_gcslots.hpp"

/*
Frame layout:
   <RDX_MAX_ALIGNMENT padding> <Opstack> <Locals> <Previous frame data>
   ^-- FrameBase (on calls, location varies)      ^--- BP (aligned to RDX_MAX_ALIGNMENT)

    <Parameters> <Return values>
	             ^--- PRV (aligned to ALIGN_RuntimeStackValue)
*/


namespace RDX
{
	template<class _TindexCharType, class _TactualCharType> class StaticLookupStringKey;
	template<class _Tkey, class _TlookupType> class StaticLookupTable;

	namespace ObjectManagement
	{
		struct IObjectManager;
		struct ISerializer;
		struct IPackageHost;
	}

	namespace Programmability
	{
		struct Method;
		struct RuntimeStackFrame;

		enum PointerSourceType
		{
			PST_Object			= 0,
			PST_PinnedLocal		= 1,
			PST_PinnedParameter	= 2,
			PST_OffsetParameter	= 3,
			PST_Parameter		= 4,	// Is an actual parameter
			PST_Invalid			= 5,
		};

		struct Journal
		{
			void				*value;
			const Type			*type;
			bool				isActive;
			bool				isParameter;
			bool				isPointer;
			bool				isVarying;
			const void			*pointerSource;		// Other value in the frame that this pointer was based on
			const String		*name;
			PointerSourceType	pointerSourceType;
		};

		class JournalScanState
		{
			LargeInt _currentILInstruction;
			const void *_data;
			const void *_bp;
			void *_prv;
		public:
			JournalScanState(const void *data, const void *bp, void *prv, LargeInt currentILInstruction);
			Journal NextJournal();
		};

		struct RuntimeTrace
		{
			const Method						*method;
			LargeInt							currentILInstruction;
			RuntimeStackValue					*prv;
			const RuntimeStackFrame				*bp;
			const void							*ip;

			bool TraceParent(RuntimeTrace& outTrace) const;
			LargeInt NumJournalEntries() const;
			Journal JournalEntry(LargeInt index) const;
			JournalScanState ScanJournals() const;
			void GetFileNameAndLineNumber(const String *&outFilename, LargeInt &outLineNumber) const;
		};

		struct RuntimeStackFrame
		{
			const Method						*method;
			const void							*ip;				// Location to resume at (in compiled code, NOT IL, must always match up with the patch list)
			RuntimeStackValue					*prv;
			RuntimeStackFrame					*bp;
			
			bool Trace(RuntimeTrace &outTrace) const;
		};

		// Root-level frame, only appears at points initiated from the API
		struct RuntimeStackFrameRoot : public RuntimeStackFrame
		{
			void	*insertionPoint;	// Location to insert new calls into the stack at.  If this is the end of the stack, no more information is present
			bool	aboveNative;		// True if this call is above a native call
		};

		// Root-level frame from reentry, present if insertionPoint isn't the end of the stack
		struct RuntimeStackFrameReentrant : public RuntimeStackFrameRoot
		{
			const Method		*nativeMethod;
			RuntimeStackFrame	subNativeFrame;
		};

		struct RuntimeThread
		{
			RuntimeStackFrame					frame;
			RuntimeStackFrame					precallFrame;			// Filled by Precall, copied to frame on invoke
			const Method						*activeNativeMethod;	// Currently active native method, set to NULL if it's below an interpreter stack
			Exception							*ex;
			LargeInt							stackCapacity;
			ObjectManagement::IObjectManager	*ownerObjectManager;
			void								*insertionPoint;

			struct DeserializationState
			{
				struct StoredJournal
				{
					const Type	*vType;			// Expected type
					LargeInt	stackOffset;	// Absolute offset into the stack
					Bool		isPointer;
					Bool		isVarying;
				};

				struct StoredFrame
				{
					const Method	*method;
					LargeInt currentILInstruction;
					LargeInt bpOffset;
					LargeInt prvOffset;
				};

				bool							deserializing;
				StoredJournal					*storedJournals;
				StoredFrame						*storedFrames;
				UInt8							*stackOccupationBits;
			};

			PossiblyAtomicInt					timeout;

			DeserializationState				deserializationState;

			UInt8								*stackBytes;

			virtual bool Trace(RuntimeTrace &outTrace) const;
			virtual void Precall(OperationContext *ctx, void *ppFramePointer, size_t sizeOfParameters, size_t sizeOfReturnValues);

			virtual bool Recover(OperationContext *ctx);				// Attempts to recover the current frame from an exceptional state to an exception handler
			virtual int Resume(OperationContext *ctx, int timeout);		// Resumes an active thread
			virtual void Reset();

			// Forces the timeout counter for the thread to 1, attempting to make it time out as soon as possible
			virtual void ForceTimeout() RDX_POSSIBLY_VOLATILE;
		};

		struct ThreadProcessor : public RDX::ObjectManagement::ITypeProcessor
		{
			void Finalize(void *obj, ObjectManagement::IObjectManager *objm) const;
			void MarkDependencies(ObjectManagement::IObjectManager *objm, void *obj, const StructuredType *st, bool markNative, ObjectManagement::ISerializer *ser, ObjectManagement::ScanID scanID, ObjectManagement::GCLink gcl) const;
			bool OnLoad(OperationContext *ctx, void *obj, ObjectManagement::IObjectManager *objm) const;

			static ThreadProcessor instance;
		};

		struct ThreadSerializer : public RDX::ObjectManagement::ITypeSerializer
		{
			void DeserializeTextInstance(OperationContext *ctx, ObjectManagement::IObjectManager *objm, void *instance, IO::ITextDeserializer *td, ObjectManagement::IPackageHost *host, ObjectManagement::Package *pkg) const;
			void DeserializeBinaryInstance(OperationContext *ctx, ObjectManagement::IObjectManager *objm, void *instance, IO::IFileStream *reader, ObjectManagement::IPackageHost *host, ObjectManagement::Package *pkg) const;

			void SerializeBinaryInstance(ObjectManagement::IObjectManager *objm, const void *obj, IO::IFileStream *fs, const ObjectManagement::SSIDTable *ssidTable) const;
			void SerializeTextInstance(ObjectManagement::IObjectManager *objm, const void *obj, IO::IFileStream *fs, const ObjectManagement::SSIDTable *ssidTable) const;

			void SerializeCommon(ObjectManagement::IObjectManager *objm, const void *obj, IO::IFileStream *fs, bool isText, const ObjectManagement::SSIDTable *ssidTable) const;
			void DeserializeCommon(OperationContext *ctx, ObjectManagement::IObjectManager *objm, void *instance,
				IO::IFileStream *fs, ObjectManagement::IPackageHost *host, ObjectManagement::Package *pkg, bool isText) const;

			static ThreadSerializer instance;
		};

		namespace RuntimeState
		{
			static const int AbandonFrame	= 2;
			static const int Active			= 1;
			static const int TimedOut		= 0;
			static const int Suspended		= -1;
			static const int Exception		= -2;
			static const int Exited			= -3;
		}

		struct ICodeProvider
		{
			virtual void CreateExecutable(ObjectManagement::IObjectManager *objm, OperationContext *ctx, Method *m) const = 0;
			virtual void ReleaseExecutable(ObjectManagement::IObjectManager *objm, Method *m) const = 0;
			virtual void InitializeSymbolDictionary(OperationContext *ctx, ObjectManagement::IObjectManager *objm) const = 0;
			virtual int RunMethod(OperationContext *ctx, ObjectManagement::IObjectManager *objm, const Programmability::Method *m, Programmability::RuntimeThread *thread, Programmability::RuntimeStackValue *prv) const = 0;

			virtual IPToCurrentInstructionCallback GetIPToCurrentInstructionCallback() const = 0;
			virtual InstrNumToIPCallback GetInstrNumToIPCallback() const = 0;
			virtual ResumeThreadCallback GetResumeThreadCallback(ObjectManagement::IObjectManager *objm) const = 0;
			virtual void Shutdown() = 0;
		};

		struct PrecompiledFunctionInfo
		{
			LargeInt functionNum;
			const void *compiledInstructions;
		};

		class PrecompiledCodeModule
		{
		public:
			typedef int (*PrecompiledFunction)(OperationContext *ctx, ObjectManagement::IObjectManager *objm, RuntimeThread *thread, const LargeInt *instrTable, void **providerDictionary);

		private:
			const void *_instructionTable;
			LargeInt _instructionTableSize;
			const StaticLookupTable<StaticLookupStringKey<char, char>, PrecompiledFunctionInfo> *_functionLookupTable;

		public:
			PrecompiledCodeModule(const void *instrTable, LargeInt instrTableSize, const StaticLookupTable<StaticLookupStringKey<char, char>, PrecompiledFunctionInfo> *functionLookupTable);

			const void *IPForMethod(const Method *m) const;
			bool CanRunIP(const Method *method, const void *ip) const;
			const PrecompiledFunctionInfo *GetFunctionInfo(const String *str) const;
			static LargeInt IPToCurrentInstruction(const Method *method, const void *ip);
			static const void *InstrNumToIP(OperationContext* ctx, const Method *method, LargeInt instrNum, bool *resumeAllowed);
			static int ResumeThread(OperationContext *ctx, ObjectManagement::IObjectManager *objm, RuntimeThread *t);
			static int RunNativeMethod(OperationContext *ctx, ObjectManagement::IObjectManager *objm, const Method *m, RuntimeThread *thread, RuntimeStackValue *prv);
		};

		namespace RuntimeUtilities
		{
			void *ArrayIndex(void *o, const RuntimeStackValue *rsv);
			void * RDX_DECL_API NewObjectInstance(OperationContext *ctx, ObjectManagement::IObjectManager *objm, const Type *t, const RuntimeStackValue *dimensions,
				LargeInt numDimensions);
			bool ExpandFrame(void *stackBase, void *frameBase, LargeInt stackSize, RuntimeStackFrame **newBP, bool hasInsertionPoint, bool hasNativeFrame);
			bool EnterMethodInline(ObjectManagement::IObjectManager *objm, const Method *m, RuntimeStackFrame *currentFrame, void *frameBase,
				void *stackBase, RuntimeStackValue *prv, RuntimeStackFrame *newFrame);
			bool EnterMethodRoot(ObjectManagement::IObjectManager *objm, const Method *m, RuntimeStackFrame *currentFrame, void *frameBase, void *stackBase,
				RuntimeStackValue *prv, RuntimeStackFrame *newFrame, void *recordedInsertionPoint, bool throughNative, const Programmability::Method *viaMethod, const RuntimeStackFrame *subFrame);

			class AutoRunFunction
			{
			public:
				inline AutoRunFunction(void (*initializer)())
				{
					initializer();
				}
			};
		}

		enum CommonExceptions
		{
			X_NullReferenceException,
			X_IndexOutOfBoundsException,
			X_AllocationFailureException,
			X_IncompatibleConversionException,
			X_InvalidOperationException,
			X_UnspecifiedException,
			X_StackOverflowException,
			X_DivideByZeroException,

			X_NumCommonExceptions,
		};
	}
}

#include "rdx_threading.hpp"

namespace RDX
{
	namespace Programmability
	{
		inline PrecompiledCodeModule::PrecompiledCodeModule(const void *instrTable, LargeInt instrTableSize, const StaticLookupTable<StaticLookupStringKey<char, char>, PrecompiledFunctionInfo> *functionLookupTable)
		{
			_instructionTable = instrTable;
			_instructionTableSize = instrTableSize;
			_functionLookupTable = functionLookupTable;
		}
	}
}

#endif
